/* eslint-disable @typescript-eslint/no-parameter-properties */
/* eslint-disable no-useless-constructor */
import * as _ from "lodash";
import { IMatching, IParseResult } from "../index";
import {
	ITableInfo,
	ICompletionItem,
	IStatement,
	ICursorInfo,
} from "../sql-parser";
import { matchesType } from "../../generic/typeUtils";
import { AutocompleteParseResult } from "../../generic/sql/types";
export type IMonacoVersion = "0.13.2" | "0.15.6";

export type IParserType =
	| "mysql"
	| "odps"
	| "blink"
	| "dsql"
	| "grail"
	| "emcsql";

export class DefaultOpts {
	public monacoEditorVersion: IMonacoVersion = "0.15.6";

	public parserType: IParserType = "odps";

	public language = "sql";

	constructor(public monaco: any, public snippets: any) {
		//
		this.monaco = monaco;
		this.snippets = snippets;
	}

	public onParse = (parseResult: IParseResult) => {
		//
	};

	public update?: (
		result: AutocompleteParseResult,
		suggestions: ICompletionItem[]
	) => Promise<any> = (result, suggestions) => {
		const promises: Promise<void>[] = [];
		const trackPromise = (promise: Promise<[]>): Promise<[]> => {
			const wrapped = new Promise<void>((resolve) => {
				promise
					.then((newSuggestions) => {
						if (newSuggestions && newSuggestions.length) {
							suggestions.push(...newSuggestions);
						}
						resolve();
					})
					.catch(resolve);
			});
			promises.push(wrapped);
			return promise;
		};
		trackPromise(this.handleKeywords(result));
		trackPromise(this.handleDatabases(result));
		trackPromise(this.handleTables(result));
		trackPromise(this.handleColumns(result));
		return Promise.all(promises);
	};

	async handleKeywords(
		parseResult: AutocompleteParseResult
	): Promise<ICompletionItem[]> {
		const suggestKeywords = parseResult.suggestKeywords;
		if (!suggestKeywords) {
			return [];
		}
		return suggestKeywords.map((keyword) => {
			let value = parseResult.lowerCase
				? keyword.value.toLowerCase()
				: keyword.value;
			return {
				label: value,
				insertText: value,
				documentation: "documentation",
				detail: "detail",
				kind: this.monaco.languages.CompletionItemKind.Keyword,
				sortText: keyword.weight + value,
			};
		});
	}

	// async handleColRefKeywords(parseResult,
	// 	colRefPromise: Promise<>
	// ): Promise<[]> {
	// 	const suggestColRefKeywords = parseResult.suggestColRefKeywords;
	// 	if (!suggestColRefKeywords) {
	// 		return [];
	// 	}
	// 	// Wait for the column reference type to be resolved to pick the right keywords
	// 	const colRef = await colRefPromise;
	// 	const colRefKeywordSuggestions = [];
	// 	Object.keys(suggestColRefKeywords).forEach((typeForKeywords) => {
	// 		if (
	// 			matchesType(
	// 				'',
	// 				[typeForKeywords],
	// 				[colRef.type.toUpperCase()]
	// 			)
	// 		) {
	// 			suggestColRefKeywords[typeForKeywords].forEach((keyword) => {
	// 				colRefKeywordSuggestions.push({
	// 					value: this.parseResult.lowerCase
	// 						? keyword.toLowerCase()
	// 						: keyword,
	// 					meta: MetaLabels.Keyword,
	// 					category: Category.ColRefKeyword,
	// 					popular: false,
	// 					details: {
	// 						type: colRef.type,
	// 					},
	// 				});
	// 			});
	// 		}
	// 	});
	// 	return colRefKeywordSuggestions;
	// }

	async handleDatabases(
		parseResult: AutocompleteParseResult
	): Promise<ICompletionItem[]> {
		debugger;
		const suggestDatabases = parseResult.suggestDatabases;
		if (!suggestDatabases) {
			return [];
		}
		const databaseSuggestions = [];
		try {
			let prefix = suggestDatabases.prependQuestionMark ? "? " : "";
			if (suggestDatabases.prependFrom) {
				prefix += parseResult.lowerCase ? "from " : "FROM ";
			}
			for (const dbEntry of this.snippets.dbSchema) {
				const name = dbEntry.dbName;
				if (name !== "") {
					databaseSuggestions.push({
						label: name,
						insertText: prefix + this.backTickIfNeeded(name),
						documentation: "documentation",
						detail: "detail",
						kind: this.monaco.languages.CompletionItemKind.Struct,
						sortText: "A" + name + "",
					});
				}
			}
		} catch (err) {
			console.log(err);
		}

		return databaseSuggestions;
	}

	async handleTables(
		parseResult: AutocompleteParseResult
	): Promise<ICompletionItem[]> {
		const suggestTables = parseResult.suggestTables;
		if (!suggestTables) {
			return [];
		}

		const getTableSuggestions = async (): Promise<ICompletionItem[]> => {
			let prefix = suggestTables.prependQuestionMark ? "? " : "";
			if (suggestTables.prependFrom) {
				prefix += parseResult.lowerCase ? "from " : "FROM ";
			}

			const database =
				suggestTables.identifierChain &&
				suggestTables.identifierChain.length === 1
					? suggestTables.identifierChain[0].name
					: this.snippets.selectedCatalog;

			let tableSuggestions: ICompletionItem[] = [];

			try {
				return this.snippets.getTableSuggestByDbName(database);
			} catch (err) {}

			return tableSuggestions;
		};

		return await getTableSuggestions();
	}

	async handleColumns(
		parseResult: AutocompleteParseResult
	): Promise<ICompletionItem[]> {
		const suggestColumns = parseResult.suggestColumns;
		if (!suggestColumns) {
			return [];
		}
		let columnSuggestions: ICompletionItem[] = [];
		suggestColumns.tables.forEach((table) => {
			const database =
				table.identifierChain && table.identifierChain.length === 2
					? table.identifierChain[0].name
					: this.snippets.selectedCatalog;
			const tableName =
				table.identifierChain && table.identifierChain.length === 1
					? table.identifierChain[0].name
					: table.identifierChain[1].name;
		   	
			columnSuggestions= columnSuggestions.concat(
				this.snippets.getTableColumnSuggestByTable(database, tableName)
			);
		});
		debugger;

		return columnSuggestions;
	}

	public backTickIfNeeded(identifier: string) {
		const quoteChar = "`";
		if (identifier.indexOf(quoteChar) === 0) {
			return identifier;
		}
		if (
			this.snippets.dbKeywords.some(
				(key) => key === identifier.toUpperCase()
			)
		) {
			return quoteChar + identifier + quoteChar;
		}

		if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(identifier)) {
			return quoteChar + identifier + quoteChar;
		}
		return identifier;
	}

	public getDbName(tableInfo?: ITableInfo) {
		let dbName = _.get(tableInfo, "namespace.value", "");
		if (!dbName) {
			dbName = this.snippets.selectedCatalog;
		}
		if (dbName) {
			dbName = dbName.replaceAll("`", "");
		}
		return dbName;
	}

	public onSuggestNamespaces?: () => Promise<ICompletionItem[]> = () => {
		return Promise.resolve(this.snippets.getDataBaseSuggest());
	};

	public onSuggestTableNames?: (
		tableInfo?: ITableInfo
	) => Promise<ICompletionItem[]> = (tableInfo) => {
		let dbName = this.getDbName(tableInfo);
		if (dbName) {
			return Promise.resolve([
				...this.snippets.getTableSuggestByDbName(dbName),
				...this.snippets.getDataBaseSuggest(),
			]);
		}
		return Promise.resolve(this.snippets.getDataBaseSuggest());
	};

	public onSuggestTableFields?: (
		tableInfo?: ITableInfo,
		cursorValue?: string,
		rootStatement?: IStatement
	) => Promise<ICompletionItem[]> = (tableInfo) => {
		let dbName = this.getDbName(tableInfo);
		return Promise.resolve(
			this.snippets.getTableColumnSuggestByTable(
				dbName,
				_.get(tableInfo, "tableName.value", "").replaceAll("`", "")
			)
		);
	};

	public pipeKeywords = (keywords: IMatching[]) => {
		return keywords
			.filter((matching) => {
				return matching.type === "string";
			})
			.map((matching) => {
				const value = /[a-zA-Z]+/.test(matching.value.toString())
					? _.upperCase(matching.value.toString())
					: matching.value.toString();
				return {
					label: value,
					insertText: value,
					documentation: "documentation",
					detail: "detail",
					kind: this.monaco.languages.CompletionItemKind.Keyword,
					sortText: `W${matching.value}`,
				};
			});
	};

	public onSuggestFunctionName?: (
		inputValue?: string
	) => Promise<ICompletionItem[]> = (inputValue) => {
		return Promise.resolve(
			["sum", "count"].map((each) => {
				return {
					label: each,
					insertText: each,
					sortText: `C${each}`,
					kind: this.monaco.languages.CompletionItemKind.Function,
				};
			})
		);
	};

	public onSuggestFieldGroup?: (
		tableNameOrAlias?: string
	) => ICompletionItem = (tableNameOrAlias) => {
		return {
			label: tableNameOrAlias,
			insertText: tableNameOrAlias,
			sortText: `D${tableNameOrAlias}`,
			kind: this.monaco.languages.CompletionItemKind.Folder,
		};
	};

	public onHoverTableField?: (
		fieldName?: string,
		extra?: ICompletionItem
	) => Promise<any> = (...args) => {
		return Promise.resolve([
			{ value: "onHoverTableField" },
			{
				value: `\`\`\`json\n${JSON.stringify(args, null, 2)}\n\`\`\``,
			},
		]);
	};

	public onHoverTableName?: (cursorInfo?: ICursorInfo) => Promise<any> = (
		...args
	) => {
		return Promise.resolve([
			{ value: "onHoverTableName" },
			{
				value: `\`\`\`json\n${JSON.stringify(args, null, 2)}\n\`\`\``,
			},
		]);
	};

	public onHoverFunctionName?: (functionName?: string) => Promise<any> = (
		...args
	) => {
		return Promise.resolve([
			{ value: "onHoverFunctionName" },
			{
				value: `\`\`\`json\n${JSON.stringify(args, null, 2)}\n\`\`\``,
			},
		]);
	};
}
